/*
 *  Created on: May 9, 2009
 *      Author: Ramesh Rajaby
 */

#ifndef _SYMPHONY_AST_BUILDER_
#define _SYMPHONY_AST_BUILDER_


#include "../Shared.h"
#include "AST.h"

using namespace boost;
using namespace algorithm;

namespace SymphonyAST {


class ASTBuilder {
private:
	map<string, Class*> classes;

	Class* parseClass(string& className);

	Method* parseMethod(ifstream& fin, string& line, Class* parent);

	void parseMember(string& line, Class* parent);

	Scope* parseScopeLine(ifstream& fin, string& line, Scope* parent);

	Expression* buildExpressionTree(string& exp, Node* parent);
	void decomposeExpressionTree(Expression* expression);

public:
	Class* buildAST(string baseClass) {
		return parseClass(baseClass);
	}
};


}


using namespace SymphonyAST;


inline Class* ASTBuilder::parseClass(string& classname) {
	string filename = classname + CompilerOptions::NOT_COMPILED_EXTENSION;
	std::ifstream fin(filename.c_str());

	Class* clazz = new Class;
	classes[classname] = clazz;

	clazz->setName(classname);

	string line;
	//Getting imports
	while (getline(fin, line)) {
		trim(line);
		if (line.empty())
			continue;

		//If the line starts with LOAD_EXTERNAL_CLASS keyword, remove it, else the imports are over so break
		if (line.find(LanguageKeywords::LOAD_EXTERNAL_CLASS) == 0) {
			line.erase(0, LanguageKeywords::LOAD_EXTERNAL_CLASS.length());
			trim(line);
		} else {
			break;
		}

		vector<string> imports;
		split(imports, line, is_any_of(", "), token_compress_on);
		BOOST_FOREACH(string import, imports) {
			clazz->import(import);
		} clazz->import(clazz->getName());
	}

	do {
		trim(line);

		if (line.empty()) {
			continue;
		}

		//TODO: make this part more clever
		if (line.find('(') == string::npos) {
			parseMember(line, clazz);
		} else {
			Method* method = parseMethod(fin, line, clazz);
			clazz->addMethod(method->getName(), method);
		}
	} while (getline(fin, line));

	return clazz;
}


inline Method* ASTBuilder::parseMethod(ifstream& fin, string& signature, Class* parent) {
	Method* method = new Method;
	method->parent = parent;

	trim(signature);
	regex signatureRegex("([a-z ]* )*([a-zA-z_][\\w]*)[ ]+([a-zA-z_][\\w]*)[ ]*\\((.*)\\)");
	smatch matches;
	if (!regex_match(signature, matches, signatureRegex)) {
		throw signature + " is not a valid method signature.";
	}

	string properties = matches[1];
	string returnType = matches[2];
	string name = matches[3];
	string parameters = matches[4];

	// properties
	trim(properties);
	vector<string> propertiesTokenized;
	split(propertiesTokenized, properties, is_any_of(" "), token_compress_on);
	for (int i = 0; i < (int)propertiesTokenized.size(); i++) {
		method->addProperty(propertiesTokenized[i]);
	}

	// type
	if (parent->getImportedId(returnType) >= 0) {
		method->setReturnType(returnType);
	} else {
	 	throw returnType + " is not a type.";
	}

	// name
	if (isValidWord(name)) {
		method->setName(name);
	} else {
		string message = "invalid word " + name + ".";
		throw message;
	}

	//return address
	Variable* ret = new Variable;
	ret->setType(method->getReturnTypeId());
	method->addVariable(name, ret);

	// parameters
	int i = 0;
	while (i < (int)parameters.length()) {
		method->addParameter(getNextParameter(parameters, i));
		i++;
	}


	/* Method body */
	string line;
	while(getline(fin, line)) {
		trim(line);
		if (line == LanguageKeywords::END_SCOPE) {
			break;
		} else if (startsWith(line, LanguageKeywords::SL_COMMENT) || line.empty()) {
			continue;
		}

		Scope* scope = parseScopeLine(fin, line, method);
		if (scope != NULL)
			method->addChild(scope);
	}

	return method;
}


inline void SymphonyAST::ASTBuilder::parseMember(string& line, Class* parent) {
	trim(line);

	regex memberRegex("([a-z ]* )*([a-zA-z_][\\w]*)[ ]+([a-zA-z_][\\w, ]*)");
	smatch matches;
	if (!regex_match(line, matches, memberRegex)) {
		throw "error in: " + line;
	}

	string properties = matches[1];
	string type = matches[2];
	string names = matches[3];

	//for each member on the line
	trim(names);
	vector<string> namesTokenized;
	split(namesTokenized, names, is_any_of(","), token_compress_on);

	//split properties
	trim(properties);
	vector<string> propertiesTokenized;
	split(propertiesTokenized, properties, is_any_of(" "), token_compress_on);

	BOOST_FOREACH(string name, namesTokenized) {
		Variable* member = new Variable;

		//add properties
		for (int i = 0; i < (int)propertiesTokenized.size(); i++) {
			member->addProperty(propertiesTokenized[i]);
		}

		// type
		if (parent->getImportedId(type) > 0) {
			parent->setType(parent->getImportedId(type));
		} else {
			throw type + " is not a type.";
		}

		trim(name);
		parent->addMember(name, member);

#ifdef DEBUG
cout << (properties.empty() ? "" : properties + " ") << type << " " << name << endl;
#endif
	}
#ifdef DEBUG
cout << endl;
#endif
}


inline Scope* SymphonyAST::ASTBuilder::parseScopeLine(ifstream& fin, string& line, Scope* parent) {
	trim(line);
	string firstWord = getFirstWord(line);

	if (PropertiesManager::isProperty(firstWord) || getContainingClass(parent)->getImportedId(firstWord) > 0) {
		/* DECLARATION */
		Class* containingClass = getContainingClass(parent);

		int pos = 0;
		string token;

		//deal with properties
		vector<string> properties;
		while (true) {
			pos = line.find_first_not_of(' ', pos);
			token = substring(line, pos, line.find(' ', pos));

			if (!PropertiesManager::isProperty(token))
				break;

			properties.push_back(token);
		}

		//type
		int type;
		if (containingClass->getImportedId(token) > 0) {
			type = containingClass->getImportedId(token);
		} else {
			throw "unexpected word " + token + ".";
		}

		//name
		pos = line.find(' ', pos);
		int len = line.length();
		for (; pos < len; pos++) {
			token = getNextParameter(line, pos);

			string name;
			if (token.find('=') != string::npos)
				name = getFirstWord(token);
			else
				name = token;

			Variable* variable = new Variable;
			for (int i = 0; i < (int)properties.size(); i++)
				variable->addProperty(properties[i]);
			variable->setType(type);

			Declaration* declaration = new Declaration;
			declaration->set(name, variable);

			parent->addChild(declaration);

			if (token.find('=') != string::npos)
				parent->addChild(parseScopeLine(fin, token, parent));
		}

		return NULL;
	} else if (firstWord == "if") {
		If* _if = new If;
		_if->parent = parent;

		Branch* branch = new Branch;
		string expression = substring(line, 2, line.length());
		branch->expression = buildExpressionTree(expression, _if);

		_if->addChild(branch);

		while(getline(fin, line)) {
			trim(line);
			if (line == "endif")
				break;
			if (startsWith(line, LanguageKeywords::SL_COMMENT) || line.empty())
				continue;

			firstWord = getFirstWord(line);
			if (firstWord == "elseif") {
				branch = new Branch;
				expression = substring(line, 6, line.length());
				branch->expression = buildExpressionTree(expression, _if);

				_if->addChild(branch);
			} else if (firstWord == "else") {
				branch = new Branch;

				_if->addChild(branch);
			} else {
				Scope* scope = parseScopeLine(fin, line, branch);
				if (scope != NULL)
					branch->addChild(scope);
			}
		}

		return _if;
	} else if (firstWord == "while") {
		While* _while = new While;
		string expression = substring(line, 5, line.length());

		_while->expression = buildExpressionTree(expression, parent);
		_while->parent = parent;

		while(getline(fin, line)) {
			trim(line);
			if (line == "repeat")
				break;
			if (startsWith(line, LanguageKeywords::SL_COMMENT) || line.empty())
				continue;

			Scope* scope = parseScopeLine(fin, line, _while);
			if (scope != NULL)
				_while->addChild(scope);
		}

		return _while;
	} else if (firstWord == "for") {
		For* _for = new For;
		_for->parent = parent;

		int i = 3;
		_for->var = getNextWord(line, i);
		string temp = getNextWord(line, i);

		if (temp == "from") {
			uint toPos = line.find(" upto ", 0);
			if (toPos == string::npos) {
				toPos = line.find(" downto ", 0);
			}
			string startExp = substring(line, i, toPos);
			_for->start = buildExpressionTree(startExp, parent);
			temp = getNextWord(line, i = toPos);
		} else {
			string startExp = "0";
			_for->start = buildExpressionTree(startExp, parent);
		}

		if (temp == "downto") {
			_for->up = false;
		} else if (temp == "upto") {
			_for->up = true;
		} else {
			throw "Unexpected keyword '" + temp + "' in for statement";
		}

		uint stepPos = line.find(" step ", 0);
		if (stepPos == string::npos) {
			stepPos = line.length();
		}
		string endExp = substring(line, i, stepPos);
		_for->end = buildExpressionTree(endExp, parent);

		i = stepPos;
		temp = getNextWord(line, i);
		string stepExp = temp == "step" ? substring(line, i, line.length()) : "1";
		_for->step = buildExpressionTree(stepExp, parent);

		while(getline(fin, line)) {
			trim(line);
			if (line == "loop")
				break;
			if (startsWith(line, LanguageKeywords::SL_COMMENT) || line.empty())
				continue;

			Scope* scope = parseScopeLine(fin, line, _for);
			if (scope != NULL)
				_for->addChild(scope);
		}

		return _for;
	} else if (firstWord == "return") {
		Return* r3turn = new Return;
		r3turn->parent = parent;
		string exp = getContainingMethod(r3turn)->getName() + " = " + substring(line, 7, line.length());
		r3turn->toReturn = buildExpressionTree(exp, parent);
		return r3turn;
	} else if (firstWord == "print" || firstWord == "println") {
		Print* print = new Print;
		print->parent = parent;
		string exp = substring(line, firstWord.length(), line.length());
		print->toPrint = buildExpressionTree(exp, parent);
		print->newLine = (firstWord == "println");
		return print;
	} else if (firstWord == "scan") {
		Scan* scan = new Scan;
		scan->parent = parent;
		string dest = substring(line, firstWord.length(), line.length());
		trim(dest);
		scan->dest = parent->getVariable(dest);
		return scan;
	} else {
		return buildExpressionTree(line, parent);
	}
}


inline Expression* SymphonyAST::ASTBuilder::buildExpressionTree(string& exp, Node* parent) {
	Expression* expression = new Expression;
	expression->parent = parent;
	trim(exp);

	//(expression) -> expression
	while (exp[0] == '(' && lookForClosingRoundBracket(exp, 0) == (int)exp.length()-1) {
		exp[0] = exp[exp.length()-1] = ' ';
		trim(exp);
	}

	int len = exp.length();

	if (len == 0) {
		throw "error in expression.";
	}


	/* ASSIGNMENTS: = += -= *= /= %= */
	int pos = findExcludingBracketsAndConsts(exp, "=", 1);

	if (pos < len &&
			exp[pos-1] != '=' && exp[pos-1] != '!' && exp[pos+1] != '=' && exp[pos-1] != '<' && exp[pos-1] != '>') {
		string op = exp.substr(pos-1, 2); trim(op);

		string lvalue, rvalue;
		if (isAssignmentOp(op) && op != "=") {
			lvalue = substring(exp, 0, pos-1);
			rvalue = substring(exp, pos+1, exp.length());
		} else {
			lvalue = substring(exp, 0, pos);
			rvalue = substring(exp, pos+1, exp.length());
			op = "=";
		}

		expression->op = op;
		trim(lvalue);
		expression->lvalue = lvalue;

		checkLValue(lvalue);

		expression->addChild(buildExpressionTree(rvalue, expression));

		return expression;
	}


	/* TERNARY OPERATOR ?: */
	pos = findExcludingBracketsAndConsts(exp, "?", 0);

	if (pos < len) {
		string op = "?";

		string lvalue, rvalue1, rvalue2;
		lvalue = substring(exp, 0, pos);

		int sepPos = findExcludingBracketsAndConsts(exp, ":", pos);
		if (sepPos == len) {
			throw "error in expression.";
		}

		rvalue1 = substring(exp, pos+1, sepPos);
		rvalue2 = substring(exp, sepPos+1, len);

		expression->op = op;
		expression->addChild(buildExpressionTree(lvalue, expression));
		expression->addChild(buildExpressionTree(rvalue1, expression));
		expression->addChild(buildExpressionTree(rvalue2, expression));

		return expression;
	}


	/* LOGIC OR: or */
	pos = 0;
	int temp = pos;
	while ((pos = findExcludingBracketsAndConsts(exp, " or ", pos)) < len) {
		string token = substring(exp, temp, pos);
		expression->addChild(buildExpressionTree(token, expression));

		pos += 4;
		temp = pos;
	}
	if (temp > 0) {
		string token = substring(exp, temp, pos);
		expression->addChild(buildExpressionTree(token, expression));
		expression->op = "||";
		return expression;
	}


	/* LOGIC AND: and */
	pos = 0;
	temp = pos;
	while ((pos = findExcludingBracketsAndConsts(exp, " and ", pos)) < len) {
		string token = substring(exp, temp, pos);
		expression->addChild(buildExpressionTree(token, expression));

		pos += 4;
		temp = pos;
	}
	if (temp > 0) {
		string token = substring(exp, temp, pos);
		expression->addChild(buildExpressionTree(token, expression));
		expression->op = "&&";
		return expression;
	}


	/* EQUALITY: ==, !=, is */
	pos = len;
	temp = pos;
	int eq = rfindExcludingBracketsAndConsts(exp, "==", pos);
	int deq = rfindExcludingBracketsAndConsts(exp, "!=", pos);
	int is = rfindExcludingBracketsAndConsts(exp, " is ", pos);
	pos = max(eq, max(deq, is));

	if (pos != -1) {
		string lvalue = substring(exp, 0, pos - (pos == is ? 2 : 1));
		string rvalue = substring(exp, pos+1, len);
		if (pos == eq)
			expression->op = "==";
		else if (pos == deq)
			expression->op = "!=";
		else
			expression->op = "is";

		expression->addChild(buildExpressionTree(lvalue, expression));
		expression->addChild(buildExpressionTree(rvalue, expression));
		return expression;
	}


	/* RELATIONAL: < <= > >= */
	pos = len;
	int l = rfindExcludingBracketsAndConsts(exp, "<", pos);
	int le = rfindExcludingBracketsAndConsts(exp, "<=", pos);
	int g = rfindExcludingBracketsAndConsts(exp, ">", pos);
	int ge = rfindExcludingBracketsAndConsts(exp, ">=", pos);
	pos = max(max(l, le), max(g, ge));

	if (pos != -1) {
		string lvalue = substring(exp, 0, pos - (pos == le || pos == ge));
		string rvalue = substring(exp, pos+1, len);
		if (pos == l)
			expression->op = "<";
		else if (pos == le)
			expression->op = "<=";
		else if (pos == g)
			expression->op = ">";
		else if (pos == ge)
			expression->op = ">=";

		expression->addChild(buildExpressionTree(lvalue, expression));
		expression->addChild(buildExpressionTree(rvalue, expression));
		return expression;
	}


	/* ADDITIVE: + - */
	pos = 0;
	temp = pos;
	while (pos < len) {
		int add = findExcludingBracketsAndConsts(exp, "+", pos);
		int sub = findExcludingBracketsAndConsts(exp, "-", pos);
		pos = min(add, sub);

		if ((temp == 0 && pos == len) || pos == 0) //no additive ops found
			break;

		if ((pos == add && exp[pos+1] == '+') ||
			(pos == sub && exp[pos+1] == '-')) {
			pos += 2;
			continue;
		}

		char prev = rgetNextNonWhiteChar(exp, pos);
		if (pos == sub && (prev == '*' || prev == '/' || prev == '%')) {
			pos++;
			continue;
		}

		expression->op = "+";
		string operand = substring(exp, temp, pos);
		Expression* newExp = buildExpressionTree(operand, expression);
		newExp->parentOpFlag = (pos == add) ? "+" : "-";
		expression->addChild(newExp);

		pos++;
		temp = pos;
	}
	if (temp > 0)
		return expression;


	/* MULTIPLICATIVE: * / %  */
	pos = 0;
	temp = pos;
	while (pos < len) {
		int mul = findExcludingBracketsAndConsts(exp, "*", pos);
		int div = findExcludingBracketsAndConsts(exp, "/", pos);
		int mod = findExcludingBracketsAndConsts(exp, "%", pos);
		pos = min(mul, min(div, mod));

		if (temp == 0 && pos == len) //no multiplicative ops found
			break;

		expression->op = "*";
		string operand = substring(exp, temp, pos);
		Expression* newExp = buildExpressionTree(operand, expression);
		if (pos == mul)
			newExp->parentOpFlag = "*";
		else if (pos == div)
			newExp->parentOpFlag = "/";
		else if (pos == mod)
			newExp->parentOpFlag = "%";
		expression->addChild(newExp);

		pos++;
		temp = pos;
	}
	if (temp > 0)
		return expression;


	/* UNARY: -x +x ++x --x*/
	if (exp.length() > 1) {
		if (exp[0] == '+' && exp[1] != '+') {
			exp[0] = ' ';
			trim(exp);
		}

		if (exp[0] == '-' && exp[1] != '-') {
			string operand = substring(exp, 1, exp.length());
			if (!getConstantType(operand)) {
				expression->op = "-x";
				expression->addChild(buildExpressionTree(operand, expression));
				return expression;
			}
		}

		if (startsWith(exp, "++")) {
			expression->op = "++x";
			string operand = substring(exp, 2, exp.length());
			expression->addChild(buildExpressionTree(operand, expression));
			return expression;
		}

		if (startsWith(exp, "--")) {
			expression->op = "--x";
			string operand = substring(exp, 2, exp.length());
			expression->addChild(buildExpressionTree(operand, expression));
			return expression;
		}
	}


	/* METHOD CALL */
	pos = exp.find('(', 0);
	if (pos != (int)string::npos) {
		string name = substring(exp, 0, pos);

		string par;
		do {
			par = getNextParameter(exp, ++pos);
			if (!par.empty()) {
				expression->addChild(buildExpressionTree(par, expression));
			}
		} while (!par.empty());

		expression->op = "call";

		expression->lvalue = name;

		return expression;
	}


	expression->lvalue = exp;
	return expression;
}


#endif
